/*
Copyleft 2006 by T.S.Yo
*/

package exp.parity;
import exp.util.*;
import ec.app.parity.*;
import ec.*;
import ec.util.*;
import ec.coevolve.*;
import ec.vector.*;
import ec.simple.*;
import ec.gp.*;
import ec.gp.koza.*;

/* 
 * MyParity.java
 * 
 * Created: 2006.11.03
 * By: T.S.Yo
 */
 
/**
 * MyParity extends Parity for <i>n</i>-[even|odd]-Parity problems.
 *
 * @author T.S.Yo
 * @version 1.0 
 */

public class MyParity2 extends Parity implements GroupedProblemForm
{
//----------------------------------------------------------------------
    /* Parameters for Problem*/
    //public boolean doEven;
    //public int bits;  // data bits
    public int testPoints;
    public int pubThreadNum;
    
    public Object clone()
    {
        MyParity myobj = (MyParity) (super.clone());
        myobj.input = (ParityData)(input.clone());
        return myobj;
    }
//----------------------------------------------------------------------
/** Setup parameters */
    public void setup(final EvolutionState state, final Parameter base)
    {
        // very important, remember this
        super.setup(state,base);
        /** Create test points */
        selectObjTP();
        //MyExpOutput myout = new MyExpOutput(state);
    }
    
//----------------------------------------------------------------------
// Create objective test points for evaluation 
    public void selectObjTP()
    {
        // totalSize has been verified to be between 2^2~31 in super.setup()
        testPoints = totalSize;
    }
//----------------------------------------------------------------------
// preprocess and postprocess
    public void preprocessPopulation( final EvolutionState state, Population pop )
    {
        // Set fitness = 0
        for( int i = 0 ; i < pop.subpops.length ; i++ )
            for( int j = 0 ; j < pop.subpops[i].individuals.length ; j++ )
                ((SimpleFitness)(pop.subpops[i].individuals[j].fitness)).setFitness( state, 0, false );
        // Set evluated = false
        for( int i = 0 ; i < pop.subpops.length ; i++ ){
            for( int j = 0 ; j < pop.subpops[i].individuals.length ; j++ )
            {
                pop.subpops[i].individuals[j].evaluated = false;
            }    
        }
    }

    public void postprocessPopulation( final EvolutionState state, Population pop )
    {
        // Evaluate solutions againt objective test points
        int ntp = testPoints;
        int nsol = pop.subpops[1].individuals.length;
        float[] objf = evalObjFitness(state, pop);
        
        // Output obj/subj fitness
        MyExpOutput myout = new MyExpOutput(state);
        myout.printFit(state, pop, objf);
        
        // Set evluated = true
        for( int i = 0 ; i < pop.subpops.length ; i++ ){
            for( int j = 0 ; j < pop.subpops[i].individuals.length ; j++ )
            {
                pop.subpops[i].individuals[j].evaluated = true;
            }    
        }
        
        // Check GP tree size for testing
        int GPTreeSize = 0;
        for( int j = 0 ; j < pop.subpops[1].individuals.length ; j++ )
        {
            GPTreeSize += pop.subpops[1].individuals[j].size();
        }
        int numTest = 0;
        for( int j = 0 ; j < pop.subpops[0].individuals.length ; j++ )
        {
            BitVectorIndividual temp = (BitVectorIndividual)pop.subpops[0].individuals[j];
            boolean[] bitVector = temp.genome;
            numTest += numOfTrue(bitVector);
        }
        System.out.println(state.generation+"  ATS: " +
                           ((float)GPTreeSize/(float)pop.subpops[1].individuals.length) +
                           "  N-INT: " + numTest);

    }
//----------------------------------------------------------------------
/** Evaluation */
    /* Evaluate one individual against one testpoint */
    public int eval1on1(final EvolutionState state,
                        final Individual ind,
                        final int inBits,
                        final int threadnum)
    {
        // first, is #bits even or odd?
        int tb = 0;
        for(int b=0;b<numBits;b++)      // from 0 to length of bit-vector, >>>: bit shift, &: bit AND
            tb += (bits >>> b) & 1;     //   AND on each bit pair of the bit-vector, => tb=1 if odd number of 1
        tb &= 1;                        // now tb is 1 if we're odd, 0 if we're even
        
        this.bits = inBits;
        ((GPIndividual)ind).trees[0].child.eval(state,threadnum,input,stack,((GPIndividual)ind),this);

        if ((doEven && ((input.x & 1) != tb)) ||((!doEven) && ((input.x & 1) == tb)))
        {    return(1); }
        else
        {    return(0); }
    }

    /* Evaluates the individuals found in ind together. */
    public void evaluate(final EvolutionState state,
                         final Individual[] ind,  // the individuals to evaluate together
                         final boolean[] updateFitness,  // should this individuals' fitness be updated?
                         final boolean countVictoriesOnly, // can be neglected in cooperative coevolution
                         final int threadnum)
   {
        // Check number of number individuals: two
        if( ind.length != 2 || updateFitness.length != 2 )
            state.output.fatal( "The InternalSumProblem evaluates only two individuals at a time." );

        // Check genome type: boolean array and GPIndividual
        if( ! ( ind[0] instanceof BitVectorIndividual ) )
            state.output.fatal( "The test-point individuals in MyParity problem should be BitVectorIndividuals." );
        if( ! ( ind[1] instanceof GPIndividual ) )
            state.output.fatal( "The solution individuals in MyParity problem should be GPIndividuals." );

        // Retrieve the bit-vector
        BitVectorIndividual temp = (BitVectorIndividual)ind[0];
        boolean[] bitVector = temp.genome;

        //GPIndividual temp2 = (GPIndividual)ind[1];
        Individual temp2 = ind[1];

        // Evaluate against all possible test points
        boolean test[] = new boolean[totalSize];
        for(bits=0;bits<totalSize;bits++)
        {
            int tb = 0;
            // first, is #bits even or odd?
            for(int b=0;b<numBits;b++)
                tb += (bits >>> b) & 1;
            tb &= 1;  // now tb is 1 if we're odd, 0 if we're even

            ((GPIndividual)ind[1]).trees[0].child.eval(
                    state,threadnum,input,stack,((GPIndividual)ind[1]),this);

            if ((doEven && ((input.x & 1) != tb)) ||((!doEven) && ((input.x & 1) == tb)))
                test[bits]=true;
            else
                test[bits]=false;
        }
        
        // Calculate interaction
        int numTest = numOfTrue(bitVector);
        float interaction = 0.0F;
        for(int z=0;z<bitVector.length;z++)
        {
            if(bitVector[z] && test[z])
                interaction+=1.0F;
        }
        interaction/=((float)numTest);
    
        // Update fitness
        //if( updateFitness[0] ){
        //    ((SimpleFitness)(ind[0].fitness)).setFitness( state, (float)(-interaction), false );
        //}
        if( updateFitness[1] ){
            //KozaFitness f = ((KozaFitness)ind[1].fitness);
            //f.setStandardizedFitness(state, (float)(interaction));
            //f.hits = interaction;
            ((SimpleFitness)(ind[1].fitness)).setFitness( state, (float)(interaction), false );
        }
        
        // Save threadnum for postprocessing
        //pubThreadNum = threadnum;
   }
  
  // Evaluate objective fitness
  public float[] evalObjFitness( final EvolutionState state, 
                                 final Population pop )
  {
        // Evaluate solutions againt objective test points
        int ntp = testPoints;
        int nsol = pop.subpops[1].individuals.length;
        float[] objFit = new float[nsol];
        // For retrieving rules
        GPIndividual temp = new GPIndividual();

        // Output
        for (int j = 0; j<(nsol); j++)        // the first nsol-1 solutions
        {
            //objFit[j] = 0;
            // Copy the solution
            temp = (GPIndividual)pop.subpops[1].individuals[j].clone();
            // Evaluate against all-test-points
            
            for (int i = 0; i < ntp; i++)
            {
               this.bits = i;
               objFit[j] += (float)eval1on1(state, temp, i, 0);
            }
            objFit[j] /= (float)ntp;   // normalized
            
            //super.evaluate(state, temp, pubThreadNum);
            //objFit[j] = (float)((KozaFitness)temp.fitness.hits)/(float)ntp;
        }
        return (objFit);
  }

//----------------------------------------------------------------------
// Supplemental methods: data-type convertion
//----------------------------------------------------------------------
    /* Return the number of "1"s in a bit-vector */
    public int numOfTrue(final boolean[] ba)
    {
        int count = 0;
        for(int j=0; j<ba.length; j++)
        {
            if(ba[j]) count++;
        }
        return count;
    }
    
    /* Convert a integer to a bit-string */
    public boolean[] intToBitArray(final int bitLength, final int x)
    {
        boolean[] bitArray = new boolean[bitLength];
        int tmp = x;
        // Convert i into n-bit binary string
        for(int j=bitLength; j>0; j--)
        {
            bitArray[j-1] = (tmp%2==1);     // If the remainder is 1, then this bit is 1
            tmp = tmp/2;                    // divided by 2 and drop remainder, to move one bit left-ward
        }
        return bitArray;
    }

    /* Convert a bit-string (stored in a int-string) to an integer*/
    public int bitArrayToInt(final boolean[] bitArray)
    {
        int x = 0;
        int bit=0;
        int bl = bitArray.length;
        // Convert bitArray to an integer:
        //   x = 2^n*bitArray[0] + 2^(n-1)*bitArray[1] +....+ 2^0*bitArray[n]
        for(int j = 0; j <bl; j++)
        {
            bit = (bitArray[j]? 1:0);
            x += bit * (int)Math.pow(2.,(double)(bl-j-1));  
        }
        return x;
    }

}
